#define _CRT_SECURE_NO_DEPRECATE

#include "bmp2g9b.h"
#include <iostream>
#include "compress.h"
#include <stdio.h>
using namespace std;

/*************************************************************/
/* Compress a G9B file                                       */
/*************************************************************/

bool Compress(char* fileName, DWORD dataSize)
{
	FILE*       file;
	G9B_HEADER  g9bHeader;
	char*       pStr=NULL;
	BYTE*       buffer=NULL, *palette=NULL;
	char*       fileout=NULL;
	int         compressed_size;

	file=fopen(fileName,"rb");
	if(file == NULL)
	{
		cout << "*** Error opening file" << endl;
		return false;
	}
	fread(&g9bHeader,1,sizeof(G9B_HEADER),file);
	if ( g9bHeader.info.nrColors )
	{
		palette = (BYTE*)malloc(g9bHeader.info.nrColors * 3);
       	fread(palette,1,g9bHeader.info.nrColors * 3,file);
	}

	buffer = (BYTE*)malloc(dataSize);
	fread(buffer,1,dataSize,file); 
    fclose(file);
	pStr=strrchr(fileName,'.');
	strcpy(pStr,".RAW");

	fileout = (char*)malloc(strlen(fileName)+1);
	strcpy(fileout, fileName);
	pStr=strrchr(fileout,'.');
	strcpy(pStr,".PCK");

    file=fopen(fileName,"wb");
	fwrite(buffer,1,dataSize,file);
    fclose(file);
	free(buffer);

	compressed_size = compress(fileName, fileout, 16000);
	
	buffer = (BYTE*)malloc(compressed_size);
    file=fopen(fileout,"rb");
	if(file==NULL) return false;
	fread(buffer,1,compressed_size,file);
    fclose(file);

	_unlink( fileName );
	_unlink( fileout );

	g9bHeader.info.compression = 1;
	g9bHeader.info.dataSize[0] = compressed_size & 255;
	g9bHeader.info.dataSize[1] = (compressed_size >> 8) & 255;
	g9bHeader.info.dataSize[2] = (compressed_size >> 16) & 255;

	pStr=strrchr(fileName,'.');
	strcpy(pStr,".G9B");
	
    file=fopen(fileName,"wb");
    fwrite(&g9bHeader,1,sizeof(G9B_HEADER),file);
	if ( g9bHeader.info.nrColors )
	{
		fwrite(palette,1, g9bHeader.info.nrColors * 3,file);
		free(palette);
	}	
	fwrite(buffer,1,compressed_size,file);
    fclose(file);

	free(buffer);
	return true;
}
/*************************************************************/
/* Generate patterns functions                               */
/*************************************************************/

void MakePatterns16Bit(BYTE* pG9bData,DWORD width,DWORD height,DWORD patX,DWORD patY)
{
   BYTE*	   temp=NULL,*buffer=NULL;
   BYTE*       pLine;
   DWORD      offsetX=0;
   DWORD      offsetY=0;
   buffer=(BYTE*)malloc(width*height*2);
   temp=buffer;
   for (DWORD nrYpat=0;nrYpat<(height/patY);nrYpat++)
   { 
      for (DWORD nrXpat=0;nrXpat<(width/patX);nrXpat++)
	  {
        pLine=pG9bData+(((width*(nrYpat*patY))+(nrXpat*patX))*2);
        for(DWORD y=0;y<patY;y++) 
		{
	       memcpy(temp,pLine,patX*2);
	       temp+=patX*2;
	       pLine+=width*2;
		}
	  }
   }
   memcpy(pG9bData,buffer,width*height*2);
   free(buffer);
}

/*************************************************************/
/* Conversion routines 24 bit -> 15 bit                      */
/*************************************************************/
void Convert24To15Bit(DWORD width, DWORD height,BYTE* pBitmap,BYTE* pG9bData)
{
   BYTE b,g,r,b15,g15,r15;
   DWORD nrPixels=height*width;
   for (DWORD pixel=0;pixel<nrPixels;pixel++)
   {
         b=*pBitmap++;
         g=*pBitmap++;
         r=*pBitmap++;
         b15=b>>3;
		 if ( ((b&7)>3) && (b<31) ) b15++;
         g15=g>>3;
	 	 if ( ((g&7)>3) && (g<31) ) g15++;
         r15=r>>3;
	 	 if ( ((r&7)>3) && (r<31) ) r15++;

         *pG9bData++=b15|(r15<<5);
	     *pG9bData++=(g15<<2)|(r15>>3);
   }
}

/*************************************************************/
/* BMP load                                                  */
/*************************************************************/

bool LoadImage(char* fileName, BYTE **pData,BYTE** pPalleteData,BYTE* pBitDepth,BYTE* pNrColors,DWORD* pWidth ,DWORD* pHeight)
{
	DWORD			 nrColors;
	bool             status;
	DWORD			 bmLineSize;
	DWORD            lineSize;
	FILE*            pFile;
	BITMAPFILEHEADER bmHeader={0};
	BITMAPINFOHEADER bmpi={0};
	RGBQUAD          bmpPalette[256];
	unsigned char*   pImageBuffer;
	
	if ((fileName == NULL) || (pData == NULL) || (pBitDepth==NULL) || (pWidth==NULL) || (pHeight==NULL)) return false;
	
	pFile = fopen(fileName, "rb");
	if (pFile==NULL)
	{
		cout << "*** Coudln't open file" << endl;
		return false;
	}
	*pData = NULL;

    fread(&bmHeader,1,sizeof(BITMAPFILEHEADER),pFile);
	if(strncmp((char*)&bmHeader.bfType,"BM",2)==0)
	{
		fread(&bmpi,1,sizeof(BITMAPINFOHEADER),pFile);
		*pBitDepth = (BYTE)bmpi.biBitCount;
		*pWidth    = bmpi.biWidth;
		*pHeight   = bmpi.biHeight;

		// Load palette and convert it to 15bit palette
		if(bmpi.biClrUsed)
		{
		   DWORD j=0;
		   fread(&bmpPalette,1,bmpi.biClrUsed * sizeof(RGBQUAD),pFile);
		   // Never more then 64 colors are converted
		   if(bmpi.biClrUsed>64) nrColors=64;
		   else nrColors=bmpi.biClrUsed;
		   BYTE* pPalette = new BYTE[nrColors*3];
		   for (DWORD i=0;i<nrColors;i++)
		   {
			  pPalette[j++]=bmpPalette[i].rgbRed>>3;
			  pPalette[j++]=bmpPalette[i].rgbGreen>>3;
			  pPalette[j++]=bmpPalette[i].rgbBlue>>3;
		   }
		   *pPalleteData = pPalette;
		   *pNrColors = (BYTE)nrColors;
		}
		else
		{
			*pPalleteData=NULL;
			*pNrColors=0;
		}
		// Allocate image buffer
		pImageBuffer     = new unsigned char[(bmpi.biWidth *  bmpi.biHeight * bmpi.biBitCount)/8];
		*pData           = pImageBuffer;
	
		// Calculate bmp line size
		bmLineSize=bmpi.biSizeImage/bmpi.biHeight;
		// Line size 'converted' image
		lineSize = (bmpi.biWidth * bmpi.biBitCount) / 8;
      
        // Calculate padding bytes  
		DWORD paddingBytes=lineSize%4;
		if(paddingBytes) paddingBytes=4-paddingBytes;

		// set file pointer on last line
		pImageBuffer+=(bmpi.biHeight-1)*lineSize;
		for (LONG line=0;line<bmpi.biHeight;line++)
		{
			DWORD data;
			fread(pImageBuffer,1,lineSize,pFile);
			fread(&data,1,paddingBytes,pFile);
			pImageBuffer-=lineSize;
		}
		status = true;
	}	
	else status = false;
	fclose(pFile);
	return status;
}

/*************************************************************/
/* G9b routines                                              */
/*************************************************************/

FILE* CreateG9bHeader(char* fileName,BYTE bitDepth,BYTE colorType, BYTE nrColors,DWORD width,DWORD height,DWORD* pDataSize)
{
	FILE* file;
	G9B_HEADER g9bHeader;
    strncpy(g9bHeader.g9bID,"G9B",3);
	g9bHeader.headerSize=sizeof(G9B_INFO_HEADER);
	g9bHeader.info.bitDepth=bitDepth;
	g9bHeader.info.colorType=colorType;
	g9bHeader.info.nrColors=nrColors;
	g9bHeader.info.compression=0;
	g9bHeader.info.width=(WORD)width;
	g9bHeader.info.height=(WORD)height;
	*pDataSize=(width*height*bitDepth)>>3;
	g9bHeader.info.dataSize[0]=(BYTE)(*pDataSize)&0xff;
	g9bHeader.info.dataSize[1]=(BYTE)((*pDataSize)>>8)&0xff;
	g9bHeader.info.dataSize[2]=(BYTE)((*pDataSize)>>16)&0xff;
	file=fopen(fileName,"wb");
	if(file) fwrite(&g9bHeader,1,sizeof(G9B_HEADER),file);
	return file;
}

void ProcessImage(char* fileName,bool compression)
{
	BYTE* pImageData;
	BYTE* pG9bData;
	BYTE* pPaletteData;
	BYTE  bitDepth,g9bBitDepth;
	DWORD width;
	DWORD height;
	BYTE  nrColors;
    DWORD dataSize;
	BYTE  colorType=0;

	if(!LoadImage(fileName,&pImageData,&pPaletteData,&bitDepth,&nrColors,&width,&height)) return;
	// Set extention to .G9B 
	char* pStr=strrchr(fileName,'.');
	if(pStr) strcpy(pStr,".G9B");
	else strcat(fileName,".G9B");

	// Write G9B header
	switch(bitDepth)
	{
		case 24: g9bBitDepth=16;break; 
		case  8: g9bBitDepth=8; break;
		case  4: g9bBitDepth=4; break;
	}

	FILE* g9bFile = CreateG9bHeader(fileName,g9bBitDepth,colorType,nrColors,width,height,&dataSize);
	pG9bData = new BYTE[dataSize];
	if(g9bFile == NULL)
	{
		cout << "*** Error creating G9B file" << endl;
		return;
	}
	switch(bitDepth)
	{
		case 24: Convert24To15Bit(width,height,pImageData,pG9bData);break; 
		case  8:    break;
		case  4:    break;
	}
	fwrite(pG9bData,1,dataSize,g9bFile);
	delete pImageData;
	delete pG9bData;
	if(pPaletteData) delete pPaletteData;
	fclose(g9bFile);
	if(compression) Compress(fileName,dataSize);
}

int main(int argc,char* argv[])
{
	// Default options
	bool enableCompression = true;
    bool enablePatternMode = false;
    bool enableRoi         = false;
	char fileName[256];
    DWORD fileIndex        = 0;

	cout << "TB - Windows Bitmap to Gfx9000 bitmap converter V0.5" << endl << endl; 
   
	if (argc==1)
	{
		cout << "Usage :" << endl;
		cout << "  - bmp2g9b [d][path]filename.ext [-b]" << endl;
		cout << "    - Input File can be bmp and g9b" << endl;
		cout << " -b    = Disable bitbuster compression" << endl;
		cout << " -pX,Y = Enable bitmap pattern mode" << endl; 
		cout << "       - X = Pattern X size" << endl;
		cout << "       - Y = Pattern Y size" << endl;
		cout << " -rL,T,W,H = Roi mode" << endl; 
		cout << "       - L = Left" << endl;
		cout << "       - T = Top" << endl;
		cout << "       - W = Width" << endl;
		cout << "       - H = Height" << endl << endl;
		return 0;
	}

    // Scan command options
	for (int i = 1; i < argc; i++)
	{
		// Check on - and / for option
		if ((argv[i][0]=='-') || (argv[i][0]=='/'))
        {
		   // Get character and upercase
		   char option=argv[i][1]&0xdf;
		   switch(option)
		   {	
				 case 'B': enableCompression = false;
						   break;
	 			 case 'P': enablePatternMode = true;
		   				   // GetPatSize(&argv[i][2],&patX,&patY);
						   break;
				 case 'R': enableRoi=true;
						   // GetRoiSize(&argv[i][2],&left,&top,&width,&height);
						   break;
				 default: cout << "*** Invalid option" << endl; 
						   return 0;
		   }		
		}
		// Otherwise the argument is the file name
		else
		{
			strcpy(fileName,argv[i]);
		}
	}
	ProcessImage(fileName,enableCompression);
	return 0;
}